package com.bank; // 声明包名，定义类所属的包

import java.util.Objects; // 导入Objects工具类，用于equals和hashCode方法

/**
 * 银行账户类
 * 提供基本的银行账户功能，包括存款、取款、转账等操作
 * 
 * @author 银行系统开发团队
 * @version 1.0.0
 */
public class Account { // 定义公共类Account，表示银行账户
    private final String id;        // 账户ID字段，使用final修饰确保不可变性
    private final String name;      // 账户持有人姓名字段，使用final修饰确保不可变性
    private double balance;          // 账户余额字段，使用double类型存储金额，可变
    
    /**
     * 构造函数
     * @param id 账户ID，不能为空
     * @param name 账户持有人姓名，不能为空
     * @param initialBalance 初始余额，不能为负数
     * @throws IllegalArgumentException 当参数无效时抛出
     */
    public Account(String id, String name, double initialBalance) { // 构造函数，接收三个参数初始化账户
        // 验证账户ID
        if (id == null || id.trim().isEmpty()) { // 检查id是否为null或去除空格后为空字符串
            throw new IllegalArgumentException("账户ID不能为空"); // 抛出非法参数异常
        }
        
        // 验证账户持有人姓名
        if (name == null || name.trim().isEmpty()) { // 检查name是否为null或去除空格后为空字符串
            throw new IllegalArgumentException("账户持有人姓名不能为空"); // 抛出非法参数异常
        }
        
        // 验证初始余额
        if (initialBalance < 0) { // 检查初始余额是否为负数
            throw new IllegalArgumentException("初始余额不能为负数"); // 抛出非法参数异常
        }
        
        this.id = id.trim(); // 将去除首尾空格的id赋值给实例变量
        this.name = name.trim(); // 将去除首尾空格的name赋值给实例变量
        this.balance = initialBalance; // 将初始余额赋值给实例变量
    }
    
    /**
     * 获取账户编号
     * @return 账户编号
     */
    public String getId() { // 公共方法，返回账户ID
        return id; // 返回私有字段id的值
    }
    
    /**
     * 获取账户持有人姓名
     * @return 账户持有人姓名
     */
    public String getName() { // 公共方法，返回账户持有人姓名
        return name; // 返回私有字段name的值
    }
    
    /**
     * 获取账户余额
     * @return 当前账户余额
     */
    public double getBalance() { // 公共方法，返回当前账户余额
        return balance; // 返回私有字段balance的值
    }
    
    /**
     * 存款操作
     * @param amount 存款金额，必须大于0
     * @throws IllegalArgumentException 当存款金额无效时抛出
     */
    public void deposit(double amount) { // 公共方法，执行存款操作
        // 验证存款金额
        if (amount <= 0) { // 检查存款金额是否小于等于0
            throw new IllegalArgumentException("存款金额必须大于0"); // 抛出非法参数异常
        }
        
        // 验证金额是否为有效数字
        if (Double.isNaN(amount) || Double.isInfinite(amount)) { // 检查金额是否为NaN或无穷大
            throw new IllegalArgumentException("存款金额必须是有效数字"); // 抛出非法参数异常
        }
        
        this.balance += amount; // 将存款金额加到当前余额上
    }
    
    /**
     * 取款操作
     * @param amount 取款金额，必须大于0且不超过当前余额
     * @throws IllegalArgumentException 当取款金额无效时抛出
     * @throws InsufficientBalanceException 当余额不足时抛出
     */
    public void withdraw(double amount) throws InsufficientBalanceException { // 公共方法，执行取款操作，可能抛出余额不足异常
        // 验证取款金额
        if (amount <= 0) { // 检查取款金额是否小于等于0
            throw new IllegalArgumentException("取款金额必须大于0"); // 抛出非法参数异常
        }
        
        // 验证金额是否为有效数字
        if (Double.isNaN(amount) || Double.isInfinite(amount)) { // 检查金额是否为NaN或无穷大
            throw new IllegalArgumentException("取款金额必须是有效数字"); // 抛出非法参数异常
        }
        
        // 检查余额是否充足
        if (this.balance < amount) { // 比较当前余额与取款金额
            throw new InsufficientBalanceException( // 抛出余额不足异常
                String.format("余额不足。当前余额: %.2f, 尝试取款: %.2f", this.balance, amount) // 格式化错误信息
            );
        }
        
        this.balance -= amount; // 从当前余额中减去取款金额
    }
    
    /**
     * 转账操作
     * @param toAccount 目标账户，不能为空且不能是自己
     * @param amount 转账金额，必须大于0且不超过当前余额
     * @throws IllegalArgumentException 当参数无效时抛出
     * @throws InsufficientBalanceException 当余额不足时抛出
     */
    public void transfer(Account toAccount, double amount) throws InsufficientBalanceException { // 公共方法，执行转账操作
        // 验证目标账户
        if (toAccount == null) { // 检查目标账户是否为null
            throw new IllegalArgumentException("目标账户不能为空"); // 抛出非法参数异常
        }
        
        // 验证不能向自己转账
        if (this.equals(toAccount)) { // 使用equals方法比较当前账户与目标账户是否相同
            throw new IllegalArgumentException("不能向自己转账"); // 抛出非法参数异常
        }
        
        // 验证转账金额
        if (amount <= 0) { // 检查转账金额是否小于等于0
            throw new IllegalArgumentException("转账金额必须大于0"); // 抛出非法参数异常
        }
        
        // 验证金额是否为有效数字
        if (Double.isNaN(amount) || Double.isInfinite(amount)) { // 检查金额是否为NaN或无穷大
            throw new IllegalArgumentException("转账金额必须是有效数字"); // 抛出非法参数异常
        }
        
        // 执行转账：先从当前账户取款，再向目标账户存款
        this.withdraw(amount);  // 调用取款方法，可能抛出InsufficientBalanceException异常
        toAccount.deposit(amount); // 调用目标账户的存款方法
    }
    
    /**
     * 返回账户的字符串表示
     * @return 格式化的账户信息字符串
     */
    @Override // 重写Object类的toString方法
    public String toString() { // 公共方法，返回账户的字符串表示
        return String.format("Account{id='%s', name='%s', balance=%.2f}", id, name, balance); // 使用格式化字符串返回账户信息
    }
    
    /**
     * 比较两个账户是否相等
     * 账户相等的条件是ID相同
     * @param obj 要比较的对象
     * @return 如果账户ID相同则返回true，否则返回false
     */
    @Override // 重写Object类的equals方法
    public boolean equals(Object obj) { // 公共方法，比较两个对象是否相等
        if (this == obj) return true; // 如果是同一个对象引用，直接返回true
        if (obj == null || getClass() != obj.getClass()) return false; // 如果对象为null或类型不同，返回false
        Account account = (Account) obj; // 将对象强制转换为Account类型
        return Objects.equals(id, account.id); // 使用Objects.equals比较两个账户的ID是否相同
    }
    
    /**
     * 返回账户的哈希码
     * 基于账户ID计算哈希码
     * @return 账户的哈希码
     */
    @Override // 重写Object类的hashCode方法
    public int hashCode() { // 公共方法，返回对象的哈希码
        return Objects.hash(id); // 使用Objects.hash方法基于账户ID计算哈希码
    }
} // 类定义结束